package app.server.project.main.service.service.upay.tron;

import app.server.project.core.constants.Def;
import app.server.project.core.constants.TcpCmd;
import app.server.project.core.exception.SysError;
import app.server.project.core.util.AssertUtil;
import app.server.project.core.websocket.WSMgr;
import app.server.project.main.service.constants.LuckType;
import app.server.project.main.service.db.dao.cfg.RechargeCfgDao;
import app.server.project.main.service.db.dao.user.UserInfoDao;
import app.server.project.main.service.db.dao.user.UserLuckOrderDao;
import app.server.project.main.service.db.entity.user.RechargeOrderEntity;
import app.server.project.main.service.db.entity.user.UserLuckOrderEntity;
import app.server.project.main.service.dto.CommonLuckOrderDto;
import app.server.project.main.service.dto.MyLuckOrderDto;
import app.server.project.main.service.dto.SOLRewardRateDto;
import app.server.project.main.service.dto.solanainfo.SlotResp;
import app.server.project.main.service.dto.upay.RateReq;
import app.server.project.main.service.service.recharge.PayStatus;
import app.server.project.main.service.service.recharge.RechargeType;
import app.server.project.main.service.service.upay.BalanceService;
import app.server.project.main.service.service.upay.UPayType;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArrayList;

@RequiredArgsConstructor
@Service
@Slf4j
public class USDTRateService {

    private final static long Max = 100;
    private final UserLuckOrderDao userLuckOrderDao;

    private final UserInfoDao userInfoDao;
    private final RechargeCfgDao rechargeCfgDao;
    private final BalanceService balanceService;
    private ConcurrentLinkedDeque<UserLuckOrderEntity> deque = new ConcurrentLinkedDeque<>();
    private CopyOnWriteArrayList<UserLuckOrderEntity> successLst = new CopyOnWriteArrayList<>();


    public void createOrder(RateReq req, long userId) {
        var cfg = this.rechargeCfgDao.getDataById(req.getCfgId());
        //检查金额
        AssertUtil.isTrue(this.balanceService.subBalance(userId, cfg.getConsumeNum()), SysError.Amount_Min);
        //创建订单
        var orderId = IdUtil.getSnowflakeNextId();
        var order = RechargeOrderEntity.of(orderId, userId, req.getCfgId(), RechargeType.USDT, cfg.getConsumeNum());
        order.setStatus(PayStatus.Finish);
        //创建下注订单
        UserLuckOrderEntity userLuckOrderEntity = UserLuckOrderEntity.of(orderId);
        userLuckOrderEntity.setChoice(req.getChoice());
        userLuckOrderEntity.setUserId(userId);

        userLuckOrderEntity.setOrderType(LuckType.USDTRate);
        userLuckOrderEntity.setSuccessTime(LocalDateTime.now());
        userLuckOrderEntity.setSuccessUnixTime(System.currentTimeMillis() / 1000);
        //刷新缓存
        userLuckOrderDao.updateOrder(userLuckOrderEntity);
        userLuckOrderEntity.setParam(cfg.getConsumeNum());
        //开始进入匹配队列
        deque.add(userLuckOrderEntity);
        var lst = this.userLuckOrderDao.getAll(order.getUserId(), LuckType.USDTRate, Max);
        lst.removeIf(item -> item.getId() == order.getId());
        lst.add(Def.Zero, userLuckOrderEntity);
        if (lst.size() > Max) {
            var temp = lst.subList((int) Max, lst.size());
            lst.removeAll(temp);
        }
    }


    public void init() {
        var all = userLuckOrderDao.getAllUnSuccess(LuckType.USDTRate);
        this.deque.addAll(all);
        this.successLst.addAll(this.userLuckOrderDao.getAllLuck(LuckType.USDTRate, Max));
    }

    public List<MyLuckOrderDto> getMyAllRateUSDT(long userId) {
        var lst = this.userLuckOrderDao.getAll(userId, LuckType.USDTRate, Max);
        return lst.stream().map(item -> {
            var ret = MyLuckOrderDto.of(item, UPayType.U_Rate);
            if (ret.getOrder().getSuccessTime() != null) {
                ret.getOrder().setSuccessTimeStr(ret.getOrder().getSuccessTime().plusHours(4));
            }
            return ret;
        }).toList();
    }

    public List<CommonLuckOrderDto> getAllUSDTRateCommon() {
        return successLst.stream().map(item -> {
            if (item.getSuccessTime() != null) {
                item.setSuccessTimeStr(item.getSuccessTime().plusHours(4));
            }
            return CommonLuckOrderDto.of(item, UPayType.U_Rate, this.userInfoDao.getUserInfoById(item.getUserId()).getNick());
        }).toList();
    }

    public boolean IsEmpty() {
        return this.deque.isEmpty();
    }


    public void matchOrder(SlotResp resp) {
        var firstOrder = deque.pollFirst();
        if (firstOrder == null) return;
        int max = 100;

        while (firstOrder != null && max > 0) {
            var flag = false;
            try {
                flag = this.matchChoice(firstOrder, resp);
            } catch (Exception e) {
                log.error(" rate matchChoice error", e);
            }
            var allOrders = this.userLuckOrderDao.getAll(firstOrder.getUserId(), LuckType.USDTRate, Max);
            UserLuckOrderEntity finalFirstOrder = firstOrder;
            allOrders.removeIf(item -> item.getId() == finalFirstOrder.getId());
            allOrders.add(Def.Zero, firstOrder);


            if (flag) {
                this.successLst.removeIf(item -> item.getId() == finalFirstOrder.getId());
                this.successLst.add(Def.Zero, firstOrder);
                WSMgr.broadcast(TcpCmd.USDTRate, null);
            } else {
                var noRewardRateDto = SOLRewardRateDto.of(firstOrder.getParam(), UPayType.U_Rate, firstOrder.getHash(), firstOrder.getChoice(), 0, UPayType.U_Rate);
                WSMgr.push(firstOrder.getUserId(), TcpCmd.UnUSDTRateReward, noRewardRateDto);
            }
            firstOrder = deque.pollFirst();
            max--;
        }

    }


    private void pushReward(long userId, long bet, String hash, String choice, long payout) {
        var dto = SOLRewardRateDto.of(bet, UPayType.U_Rate, hash, choice, payout, UPayType.U_Rate);
        WSMgr.push(userId, TcpCmd.USDTRateReward, dto);
    }

    public boolean matchChoice(UserLuckOrderEntity order, SlotResp resp) {
        order.setHighVal(resp.getSlot());
        order.setHash(resp.getResult().getBlockhash());
        //修改订单状态
        order.setStatus(Def.True);
        //中奖字符串
        String btcStr = resp.getResult().getBlockhash().substring(resp.getResult().getBlockhash().length() - 1);

        //检查是否中了数字
        if ("1".equals(order.getChoice())) {
            if (NumberUtil.isNumber(btcStr)) {
                this.reward(order, 6 * (order.getParam()), order.getParam(), order.getParam() * 6);
                return true;
            }
        } else {
            //检查是否小写
            if ("2".equals(order.getChoice())) {
                boolean isLowerCase = (btcStr.charAt(0) >= 'a' && btcStr.charAt(0) <= 'z');
                if (isLowerCase) {
                    this.reward(order, 2 * (order.getParam()), order.getParam(), order.getParam() * 2);
                    return true;
                }

            } else {
                //检查是否大写
                if ("3".equals(order.getChoice())) {
                    boolean isUpperCase = (btcStr.charAt(0) >= 'A' && btcStr.charAt(0) <= 'Z');
                    if (isUpperCase) {
                        this.reward(order, 2 * (order.getParam()), order.getParam(), order.getParam() * 2);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void reward(UserLuckOrderEntity order, long val, long bet, long payout) {
        this.balanceService.addBalance(order.getUserId(), val);
        order.setTonVal(val);
        this.successLst.add(order);
        pushReward(order.getUserId(), bet, order.getHash(), order.getChoice(), payout);
    }

}
